Enums
An enum (enumeration) in Rust lets you define a type that can be one of several different variants, where each variant can store different types of data.
Think of an enum as:
“A value that must be exactly one of a known set of possibilities.”
enum Direction {
North,
South,
East,
West,
}
A variable of type Direction can be only one of these four values.
Key Characteristics of Enums
| Feature | Enum |
|---|---|
| Variants | Multiple |
| Data in variants | Optional |
| Types per variant | Different allowed |
| Safety | Compile-time checked |
| Pattern matching | Powerful |
| Memory | Size = largest variant |
Creating Enum Values
let d1 = Direction::North;
let d2 = Direction::West;
Use EnumName::Variant.
Enums with Data (Very Important)
Variants Carrying Values
enum Message {
Quit,
Write(String),
Move { x: i32, y: i32 },
ChangeColor(i32, i32, i32),
}
Each variant stores different data types.
Creating Values
let m1 = Message::Quit;
let m2 = Message::Write(String::from("Hello"));
let m3 = Message::Move { x: 10, y: 20 };
let m4 = Message::ChangeColor(255, 0, 0);
Pattern Matching with Enums
Using match
fn process_message(msg: Message) {
match msg {
Message::Quit => println!("Quit"),
Message::Write(text) => println!("Text: {}", text),
Message::Move { x, y } => println!("Move to {}, {}", x, y),
Message::ChangeColor(r, g, b) =>
println!("Color: {}, {}, {}", r, g, b),
}
}
match is exhaustive — all cases must be handled.
Enums vs Structs
| Feature | Enum | Struct |
|---|---|---|
| Multiple shapes | ✅ | ❌ |
| Named fields | Optional | ✅ |
| Best for | Variants | Single structure |
Use enums when:
- A value can be one of many forms
- Each form has different data
Enums with Methods (impl)
impl Message {
fn call(&self) {
match self {
Message::Quit => println!("Quit"),
Message::Write(text) => println!("Writing {}", text),
_ => println!("Other message"),
}
}
}
Usage:
m2.call();
Option<T> – The Most Common Enum
Rust does not have null. Instead:
enum Option<T> {
Some(T),
None,
}
Example
let name: Option<String> = Some(String::from("Rust"));
let empty: Option<String> = None;
Using Option
match name {
Some(value) => println!("Name: {}", value),
None => println!("No name"),
}
Result<T, E> – Error Handling Enum
enum Result<T, E> {
Ok(T),
Err(E),
}
Example
fn divide(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
Err(String::from("Cannot divide by zero"))
} else {
Ok(a / b)
}
}
Usage:
match divide(10, 2) {
Ok(result) => println!("Result: {}", result),
Err(err) => println!("Error: {}", err),
}
Enums with if let
For simpler cases:
let msg = Message::Quit;
if let Message::Quit = msg {
println!("Exiting...");
}
Cleaner than match when you only care about one case.
Enums and Ownership
let msg = Message::Write(String::from("Hello"));
match msg {
Message::Write(text) => println!("{}", text),
_ => (),
}
text is moved here.
Memory Layout of Enums (Conceptual)
Enums store:
- A tag (which variant?)
- Data for the largest variant
Efficient and predictable.
Summary
- Enums define a type with multiple variants
- Variants can hold different data
matchensures all cases are handledOptionandResultare enums- Extremely powerful and safe